# MA策略
import talib
import module_backtest
from vnctptdType661 import *
# CTP行情库
from vnctpmd import *
import numpy as np
import globalType
import globalvar
import numba as nb
# 策略参数定义 '名称，开始值，结束值，步长'
parlist = [['short', 3, 10, 1], ['long', 10, 30, 1]]


class MyStrategy(module_backtest.VirtualAccount):
    def __init__(self, period, slippoint):
        super(MyStrategy, self).__init__(period, slippoint)
        self.close=[]

    def InsertOrder(self, InstrumentID, exchangeid, direction, offside, orderpricetype, price, vol):
        self.InsertOrder2(InstrumentID, exchangeid, direction, offside, orderpricetype, price, vol)

    # 实盘直接由回调函数驱动OnTick
    # （1）可由OnTick再触发OnKline（基于K线的数据）
    # （2）也可以在OnTick直接驱动策略
    # 放在module_backtest.py的回测默认用M1以上数据，所以驱动的是OnKline,但理论上也可以通过OnTick方法来驱动，只是必要性不大
    # 以上是一般推荐模式，但由于本项目是开源项目，可基于Python原生方法开发策略，所以用户有很大自由度。
    @nb.jit(nogil=True, nopython=True)
    def OnTick(self, tickdata, strategyname,filter):
        arg = [5, 20, 0, 0, 0, 0]
        InstrumentID = str(tickdata.InstrumentID, encoding="utf-8")
        kline = VNKlineData()
        kline.InstrumentID = tickdata.InstrumentID
        # kline.TradingDay = globalvar.md.GetKline(InstrumentID, 1)[0].TradingDay
        kline.TradingDay = int(str(globalvar.md.GetKline(InstrumentID, 1)[0].TradingDay, encoding="utf-8")  )
        kline.open = globalvar.md.GetKline(InstrumentID, 1)[0].Open
        kline.high = globalvar.md.GetKline(InstrumentID, 1)[0].High
        kline.low = globalvar.md.GetKline(InstrumentID, 1)[0].Low
        kline.close = globalvar.md.GetKline(InstrumentID, 1)[0].Close
        kline.volume = globalvar.md.GetKline(InstrumentID, 1)[0].Volume
        kline.klinetime = int(globalvar.md.GetKline(InstrumentID, 1)[0].Minutes)
        kline.money = 0
        kline.open_interest = 0
        print(str(kline.TradingDay) + "," +str(kline.open))
        self.OnKline(kline, arg, filter)
        '''
        #（1）tickdata 是 实时Tick数据
        #（2）这是从CTP Tick由vnctpmd.dll 在本机生成的K线，可以直接按下标方式取得，速度快vnctptd.dll、vnctpmd.py、vnctptd.ini
        #（3）这是从服务器获得当日K线取法，禁止频繁调用  vnklineservice.dll、vnklineservice.py、vnklineservice.ini
        #（4）这是当前显示K线图数据暂时，数据来自于（2）和（3），策略计算不用这个
        globalvar.data_kline_M1[0][globalvar.OPEN]
        globalvar.data_kline_M1[0][globalvar.HIGH]
        globalvar.data_kline_M1[0][globalvar.LOW]
        globalvar.data_kline_M1[0][globalvar.CLOSE]
        globalvar.data_kline_M1[0][globalvar.KLINETIME]
        globalvar.data_kline_M1[0][globalvar.VOL]
        globalvar.md.GetKline(tickdata.InstrumentID, 0).contents.Minutes
        str(globalvar.data_kline_M1[0][globalvar.INSTRUMENT], encoding="utf-8")
        str(globalvar.data_kline_M1[0][globalvar.TRADINGDAY], encoding="utf-8")
        '''

    # OnKline是K线数据驱动
    # 回测一般基于M1及以上周期K线数据，则直接由module_backtest.py里读取 csv文件方式直接驱动OnKline
    def OnKline(self, kdata, arg, filter):
        if arg[0] <= 0 or arg[1] <= 0:
            return
        # TradingDay = klinedata.TradingDay.decode()
        # klinetime = klinedata.klinetime.decode()
        # self.exchange=mddata.exchange.decode()
        self.InstrumentID = kdata.InstrumentID.decode()
        self.close.append(float(kdata.close))
        try:
            float_close = [float(x) for x in self.close]
        except Exception as e:
            pass
        self.MA_A = talib.MA(np.array(float_close), arg[0])
        self.MA_B = talib.MA(np.array(float_close), arg[1])
        # print('结果1:' + str(self.MA_A))
        if self.MA_A[len(self.MA_A) - 1] > self.MA_B[len(self.MA_B) - 1]:
            if self.sellvol + self.sellvol_history > 0:
                self.InsertOrder(self.InstrumentID, '', THOST_FTDC_D_Buy, THOST_FTDC_OF_Close, VN_OPT_LimitPrice,
                                 kdata.close + 1, 1)
            if self.buyvol + self.buyvol_history < 10:
                self.InsertOrder(self.InstrumentID, '', THOST_FTDC_D_Buy, THOST_FTDC_OF_Open, VN_OPT_LimitPrice,
                                 kdata.close + 1, 1)
        elif self.MA_A[len(self.MA_A) - 1] < self.MA_B[len(self.MA_B) - 1]:
            if self.buyvol + self.buyvol_history > 0:
                self.InsertOrder(self.InstrumentID, '', THOST_FTDC_D_Sell, THOST_FTDC_OF_Close, VN_OPT_LimitPrice,
                                 kdata.close - 1, 1)
            if self.sellvol + self.sellvol_history < 10:
                self.InsertOrder(self.InstrumentID, '', THOST_FTDC_D_Sell, THOST_FTDC_OF_Open, VN_OPT_LimitPrice,
                                 kdata.close - 1, 1)

    '''
    方式1（推荐）：
    #本方式数据，先从服务器获取数据，再根据本地TICK数据自动生成，
    #断网重连会从新从服务器获取K线数据，是否从服务器获取初始的数据是可以选择的，具体在VNTrader “主菜单->K线数据来源” 选择
    #本方式K线数据是存在Python的变量里，定义在 globalvar.py ，globalvar.py存储的变量是共享的全局变量，方式各个Python文件调用
    #只有 data_kline_M1  (M1周期数据)是从服务器获取或实时生成
    # 其他周期K线数据是通过Python的Pandas本地合成
    # 本地生成数据包括 data_kline_M3、data_kline_M5、data_kline_M10、data_kline_M15、data_kline_M30、data_kline_M60、data_kline_M120、data_kline_D1
    #字段分别为 ID,Data,Time,Open,Close,Low,High
    #调用方式如下
    print("data_kline_M1: " + str(data_kline_M1[0][globalvar.TRADINGDAY]))
    print("data_kline_M1: " + str(data_kline_M1[0][globalvar.KLINETIME]))
    print("data_kline_M1: " + str(data_kline_M1[0][globalvar.INSTRUMENT]))
    print("data_kline_M1: " + str(data_kline_M1[0][globalvar.OPEN]))
    print("data_kline_M1: " + str(data_kline_M1[0][globalvar.CLOSE]))
    print("data_kline_M1: " + str(data_kline_M1[0][globalvar.LOW]))
    print("data_kline_M1: " + str(data_kline_M1[0][globalvar.HIGH]))
    print("data_kline_M1: " + str(data_kline_M1[0][globalvar.VOL]))

    print("data_kline_M1: " + str(data_kline_M1[1][globalvar.TRADINGDAY]))
    print("data_kline_M1: " + str(data_kline_M1[1][globalvar.KLINETIME]))
    print("data_kline_M1: " + str(data_kline_M1[1][globalvar.INSTRUMENT]))
    print("data_kline_M1: " + str(data_kline_M1[1][globalvar.OPEN]))
    print("data_kline_M1: " + str(data_kline_M1[1][globalvar.CLOSE]))
    print("data_kline_M1: " + str(data_kline_M1[1][globalvar.LOW]))
    print("data_kline_M1: " + str(data_kline_M1[1][globalvar.HIGH]))
    print("data_kline_M1: " + str(data_kline_M1[1][globalvar.VOL]))
    方式2：
    #从开源DLL获得M1 K线数据
    print("M1 K线合约名称 ref(0): " + str(globalvar.md.GetKline(marketdata.InstrumentID, 0)[0].InstrumentID, encoding="utf-8"))
    print("M1 K线结束时间 ref(0): " + str(globalvar.md.GetKline(marketdata.InstrumentID, 0)[0].KlineTime))
    print("M1 K线 最高价 ref(0): " + str(globalvar.md.GetKline(marketdata.InstrumentID, 0)[0].High))
    print("M1 K线 最低价 ref(0): " + str(globalvar.md.GetKline(marketdata.InstrumentID, 0)[0].Low))
    print("M1 K线 收盘价 ref(0): " + str(globalvar.md.GetKline(marketdata.InstrumentID, 0)[0].Close))
    print("M1 K线 成交量 ref(0): " + str(globalvar.md.GetKline(marketdata.InstrumentID, 0)[0].Volume))
    print("M1 K线 交易日 ref(0): " + str(globalvar.md.GetKline(marketdata.InstrumentID, 0)[0].TradingDay, encoding="utf-8"))

    print("M1 K线合约名称 ref(1): " + str(globalvar.md.GetKline(marketdata.InstrumentID, 1)[0].InstrumentID, encoding="utf-8"))
    print("M1 K线结束时间 ref(1): " + str(globalvar.md.GetKline(marketdata.InstrumentID, 1)[0].KlineTime))
    print("M1 K线 最高价 ref(1): " + str(globalvar.md.GetKline(marketdata.InstrumentID, 1)[0].High))
    print("M1 K线 最低价 ref(1): " + str(globalvar.md.GetKline(marketdata.InstrumentID, 1)[0].Low))
    print("M1 K线 收盘价 ref(1): " + str(globalvar.md.GetKline(marketdata.InstrumentID, 1)[0].Close))
    print("M1 K线 成交量 ref(1): " + str(globalvar.md.GetKline(marketdata.InstrumentID, 1)[0].Volume))
    print("M1 K线 交易日 ref(1): " + str(globalvar.md.GetKline(marketdata.InstrumentID, 1)[0].TradingDay, encoding="utf-8"))
    '''
